
package hasthi.container;

import static hasthi.common.constants.WSDMConstants.CAPABILITY_CONFIGURATION;
import static hasthi.common.constants.WSDMConstants.CAPABILITY_MATRICS1;
import static hasthi.common.constants.WSDMConstants.MANAGMENT_EVNET_SINK_PARAM;
import hasthi.common.ErrorManager;
import hasthi.common.HasthiException;
import hasthi.common.Utils;
import hasthi.common.HasthiException.FaultCode;
import hasthi.common.constants.HasthiConstants;
import hasthi.config.CapabilityType;
import hasthi.config.ManagementConfigurationDocument;
import hasthi.config.ManagementConfigurationDocument.ManagementConfiguration;
import hasthi.wsdm.Capability;
import hasthi.wsdm.CapabilityDescription;
import hasthi.wsdm.ManagableResource;
import hasthi.wsdm.ManagedSystemHandle;
import hasthi.wsdm.ManagementAgentContext;
import hasthi.wsdm.systemhandle.SystemHandleVerifier;
import hasthi.wsrf.WSResource;
import hasthi.wsrf.WSResourceLocater;
import hasthi.wsrf.faults.ResourceUnknownFault;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlbeans.XmlException;
import org.w3.x2005.x08.addressing.EndpointReferenceType;

public class WSDMResourceLocater implements WSResourceLocater{
    private static Log log = LogFactory.getLog(HasthiConstants.AGENT_LOGGER_NAME);
    
    private Map<String, CapabilityType> capabilityMap = new HashMap<String, CapabilityType>(); 
    private ManagementConfiguration managementConfiguration;
    private Map<String,ManagableResource> managableResources = new ConcurrentHashMap<String,ManagableResource>();
    private ManagementAgentContext context;
    
    private Pattern resourcePattern = Pattern.compile(".*/hasthi/resource/(.*)");
    
    public WSDMResourceLocater(ManagementAgentContext context)throws HasthiException{
        try {
            this.context = context;
            //SystemHandleVerifier.verify(systemHandle);
            ManagementConfigurationDocument managementConfigurationDocument;
            if(context.getConfigurationStream() != null){
                managementConfigurationDocument =  ManagementConfigurationDocument.Factory.parse(context.getConfigurationStream());    
            }else{
                InputStream configurationStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("hasthi/wsdm/config.xml");
                managementConfigurationDocument =  ManagementConfigurationDocument.Factory.parse(configurationStream);
            }
            managementConfiguration = managementConfigurationDocument.getManagementConfiguration();
            CapabilityType[] capabilities = managementConfiguration.getCapabilityArray();
            for(CapabilityType capability:capabilities){
                capabilityMap.put(capability.getCapabilityURI(), capability);
            }
        } catch (FileNotFoundException e) {
            throw new HasthiException(e, FaultCode.InitalizationError);
        } catch (XmlException e) {
            throw new HasthiException(e, FaultCode.InitalizationError);
        } catch (IOException e) {
            throw new HasthiException(e, FaultCode.InitalizationError);
        }
    }
    
    
    public ManagableResource addResource(ManagedSystemHandle systemHandle) throws HasthiException{
        try {
           
            //set the default value for Management sink EPR
            systemHandle.setConfiguration(MANAGMENT_EVNET_SINK_PARAM, HasthiConstants.UNSPECIFIED_EPR);

            
            String systemEndpoint = systemHandle.getSystemEndPoint();
            
            ManagableResource resource = new ManagableResource(context,systemHandle);
            //Initialize the capabilities and initialize the managable resource with capabilities
            List<Capability> capabilities = new ArrayList<Capability>();
            List<String> capabilityNames = systemHandle.getSupportedCapabilities();
            for(String capabilityName:capabilityNames){
                /**
                 * Load capability description and create a associated capability object
                 */
                CapabilityType capabilityDesc = capabilityMap.get(capabilityName);
                if(capabilityDesc == null){
                    throw new HasthiException("Capability "+ capabilityName + " can not be found",FaultCode.InitalizationError);
                }
                if(capabilityName.equals(CAPABILITY_CONFIGURATION)){
                    capabilityDesc = (CapabilityType)capabilityDesc.copy();
                    addProperties(capabilityDesc, systemHandle.getSupportedConfigurationNames());
                }else if(capabilityName.equals(CAPABILITY_MATRICS1)){
                    capabilityDesc = (CapabilityType)capabilityDesc.copy();
                    addProperties(capabilityDesc, systemHandle.getSupportedMatrics());
                }
                Class capabilityImplClass = Utils.loadClass(capabilityDesc.getImplClass());
                Capability capability = (Capability)capabilityImplClass.newInstance();
                capability.init(context,resource,new CapabilityDescription(capabilityDesc));
                capabilities.add(capability);
                log.debug("INIT " +capability.getClass() + " "+  capabilityName);
            }
            resource.init(capabilities);
            
            if(systemHandle.getNotifier() != null){
                resource.addNotifier(systemHandle.getNotifier());    
            }
            for(Capability capability:capabilities){
                capability.parentResourceInitialized();
            }
            systemHandle.init(context);
            SystemHandleVerifier.verify(systemHandle);
            
            managableResources.put(resource.getName().getLocalPart(),resource);
            context.getBaseContext().getDlogger().info("Resource " +resource.getName()+" Started");
            return resource;
        } catch (InstantiationException e) {
            throw new HasthiException(e, FaultCode.InitalizationError);
        } catch (IllegalAccessException e) {
            throw new HasthiException(e, FaultCode.InitalizationError);
        } catch (ClassNotFoundException e) {
            throw new HasthiException(e, FaultCode.InitalizationError);
        }
    }
    
    public void addProperties(CapabilityType type,List<QName> propertyNames){
        if(propertyNames == null){
            return;
        }
        for(QName name:propertyNames){
            type.addNewProperty().setName(name);
        }
    }
    
    public WSResource findResource(EndpointReferenceType epr) throws ResourceUnknownFault {
        String address = epr.getAddress().getStringValue();
        Matcher matcher =resourcePattern.matcher(address);
        if(matcher.matches()){
            ManagableResource managableResource = managableResources.get(matcher.group(1));
            if(managableResource != null){
                return managableResource;    
            }
        }else{
            Iterator<ManagableResource> it = managableResources.values().iterator();
            if(it.hasNext()){
                return it.next();
            }
        }
        throw new ResourceUnknownFault(epr);
    }

    public void shutDownAllResources() {
        Collection<ManagableResource> resourceList = managableResources.values();
        for(ManagableResource resource:resourceList){
            try {
                this.context.getBaseContext().getDlogger().info("Resource "+resource.getName() + " Stopped");
                resource.stop();
            } catch (HasthiException e) {
                log.error("Fail while shutting down resources",e);
                ErrorManager.error(e);
            }
        }
//        try {
//            Thread.sleep(10000);
//            for(StackTraceElement[] st:Thread.getAllStackTraces().values()){
//                System.out.println(Arrays.toString(st));
//            }
//        } catch (InterruptedException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
    }

    public void shutDownResource(EndpointReferenceType epr) throws ResourceUnknownFault {
        shutDownAllResources();
    }
}

